package ltd.handler;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import ltd.bean.JacksonMapper;
import ltd.bean.MSGResult;
import ltd.bean.Session;
import ltd.constant.NetProtocolConstant;
import ltd.helper.ChannelGroupHelper;
import ltd.helper.RSAHelper;
import ltd.util.NetUtil;

/**
 * server端网络IO事件处理
 */
public class MyServerHandler extends ChannelInboundHandlerAdapter {
	
	public static final Charset CHARSET = Charset.forName("UTF-8");
	
	private MyControlHandler control;

	public MyServerHandler() {
		this.control = MyControlHandler.getInstance();
	}

	/**
	 * 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		//添加到channelGroup 通道组
//		MyChannelGroup.getInstance().getChGroup().add(ctx.channel());
		ChannelGroupHelper.addChGroup(ctx.channel());
//		MyChannelGroup.getInstance().getChannelMap().put(ctx.channel().id().toString(), ctx);
		ChannelGroupHelper.putChannelMap(ctx.channel().id().toString(),ctx);
		ObjectMapper mapper = JacksonMapper.getMapper();
		//创建Json，发送自己的公钥
    	ObjectNode root1 = mapper.createObjectNode();
    	root1.put("modulus", RSAHelper.getKeyBase64(RSAHelper.PUBLIC_KEY).trim().replaceAll("\r\n", ""));
    	String sendstr = root1.toString();
		//System.out.println("要发送的JSON消息内容为：" + sendstr);
    	
		sendMsg(ctx, sendstr, NetProtocolConstant.CONNECT);
	}

	/**
	 * 当客户端主动断开服务端的链接后，这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		// 从channelGroup中移除，当有客户端退出后，移除channel。
		String channelId = ctx.channel().id().toString() ;
//		MyChannelGroup.getInstance().getChGroup().remove(ctx.channel());
		ChannelGroupHelper.removeChannelFormChGroup(ctx.channel());
//		MyChannelGroup.getInstance().getChannelMap().remove(ctx.channel().id().toString());
		ChannelGroupHelper.removeChannelFormChannelMap(channelId);
//		Session session = MyChannelGroup.getInstance().getSessionMap().get(ctx.channel().id().toString());
		Session session = ChannelGroupHelper.getSession(channelId);
		if(session != null && session.isType()){
//			MyChannelGroup.getInstance().getGameMap().remove(session.getGameid());
			ChannelGroupHelper.removeGameSession(session.getGameid());
		}
//		MyChannelGroup.getInstance().getSessionMap().remove(ctx.channel().id().toString());
		ChannelGroupHelper.removeSession(channelId);
	}

	/**
	 * 简而言之就是从通道中读取数据，也就是服务端接收客户端发来的数据，但是这个数据在不进行解码时它是ByteBuf类型的
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//System.out.println("服务器读取到客户端请求...");
		ByteBuf buf = (ByteBuf) msg;
		int protocol = buf.readInt();//协议号
		//System.out.println("client protocol ===== " + protocol);
		byte[] req = null;
		if(buf.isReadable()) {
			req = new byte[buf.readableBytes()];
			buf.readBytes(req);
			//System.out.println("收到的消息:" + new String(req, "UTF-8"));
		}
		if(req != null && req.length > 0){
			ObjectMapper mapper = JacksonMapper.getMapper();
			if(protocol == NetProtocolConstant.SERVER_CONNECT_LOGINSERVER){
				JsonNode root = mapper.readTree(new String(req, "UTF-8"));
				JsonNode ipstr = root.get("iphost");
				JsonNode portstr = root.get("port");
				JsonNode gidstr = root.get("gid");
				String iphost = ipstr.asText();
				int port = portstr.asInt();
				int gid = gidstr.asInt();
//				Session session = MyChannelGroup.getInstance().getSessionMap().get(ctx.channel().id().toString());
				Session session = ChannelGroupHelper.getSession(ctx.channel().id().toString());
				session.setType(true);
				session.setIphost(iphost);
				session.setPort(port);
				session.setGameid(gid);
//				MyChannelGroup.getInstance().getGameMap().put(gid, session);
				ChannelGroupHelper.putGameSession(gid,session);

				sendMsg(ctx, "ok", NetProtocolConstant.SERVER_CONNECT_LOGINSERVER_RES);
			}else if(protocol == NetProtocolConstant.SERVER_USER_LOGIN_RES){
				String chid = new String(req, "UTF-8");
				//System.out.println("... 用户登录 session 发送至 游戏服务器  成功... 收到的回执chid：" + chid);
				control.do_LOGIN_RES(ctx.channel(), chid);
			}else if(protocol == NetProtocolConstant.HEARTBEAT_DETECTION_RES){
				//System.out.println("... 收到 心跳 回执 ...");
			}else if(protocol == NetProtocolConstant.CONNECT_LOGINSERVER){
				try {
					String msgStr = RSAHelper.doRSADecoder(req);
					this.control.doConnectServer(ctx.channel(), msgStr);
				} catch (Exception e) {
					e.printStackTrace();
					MSGResult msgres = new MSGResult();
					msgres.setData("连接登录服务器出错");
					msgres.setErr(true);
					String sendstr = mapper.writeValueAsString(msgres);
					sendMsg(ctx, RSAHelper.doRSAEncoderPri(sendstr), NetProtocolConstant.CONNECT_LOGINSERVER_RES);
				}
			}else if(protocol == NetProtocolConstant.DO_LOGIN){
				try {
					String msgStr = RSAHelper.doRSADecoder(req);
					//System.out.println("解密后的登录信息内容为：\n" + msgStr);
					this.control.USER_LOGIN(ctx.channel(), msgStr);
				} catch (Exception e) {
					e.printStackTrace();
					MSGResult msgres = new MSGResult();
					msgres.setData("解密用户登录信息出错");
					msgres.setErr(true);
					String sendstr = mapper.writeValueAsString(msgres);
					sendMsg(ctx, RSAHelper.doRSAEncoderPri(sendstr), NetProtocolConstant.DO_LOGIN_RES);
				}
			}else if(protocol == NetProtocolConstant.DO_REG){
				try {
					String msgStr = RSAHelper.doRSADecoder(req);
					//System.out.println("解密后的注册信息内容为：\n" + msgStr);
					this.control.USER_REG(ctx.channel(), msgStr);
				} catch (Exception e) {
					e.printStackTrace();
					MSGResult msgres = new MSGResult();
					msgres.setData("解密用户注册信息出错");
					msgres.setErr(true);
					String sendstr = mapper.writeValueAsString(msgres);
					sendMsg(ctx, RSAHelper.doRSAEncoderPri(sendstr), NetProtocolConstant.DO_REG_RES);
				}
			}else if(protocol == NetProtocolConstant.DO_SN){
				try {
					String msgStr = RSAHelper.doRSADecoder(req);
					//System.out.println("解密后的登录信息内容为：\n" + msgStr);
					this.control.USER_SN(ctx.channel(), msgStr);
				} catch (Exception e) {
					e.printStackTrace();
					MSGResult msgres = new MSGResult();
					msgres.setData("解密用户登录信息出错");
					msgres.setErr(true);
					String sendstr = mapper.writeValueAsString(msgres);
					sendMsg(ctx, RSAHelper.doRSAEncoderPri(sendstr), NetProtocolConstant.DO_SN_RES);
				}
			}else if(protocol == NetProtocolConstant.CHECK_SN){
				try {
					String msgStr = RSAHelper.doRSADecoder(req);
					//System.out.println("CHECK_SN ** 解密后的登录信息内容为：\n" + msgStr);
					this.control.doCheckSN(ctx.channel(), msgStr);
				} catch (Exception e) {
					e.printStackTrace();
					MSGResult msgres = new MSGResult();
					msgres.setData("解密检测SN信息出错");
					msgres.setErr(true);
					String sendstr = mapper.writeValueAsString(msgres);
					sendMsg(ctx, RSAHelper.doRSAEncoderPri(sendstr), NetProtocolConstant.CHECK_SN_RES);
				}
			}else if(protocol == NetProtocolConstant.CHECK_NAME){
				try {
					String msgStr = RSAHelper.doRSADecoder(req);
					//System.out.println("CHECK_NAME ** 解密后的名称信息内容为：\n" + msgStr);
					this.control.doCheckName(ctx.channel(), msgStr);
				} catch (Exception e) {
					e.printStackTrace();
					MSGResult msgres = new MSGResult();
					msgres.setData("解密检测名称信息出错");
					msgres.setErr(true);
					String sendstr = mapper.writeValueAsString(msgres);
					sendMsg(ctx, RSAHelper.doRSAEncoderPri(sendstr), NetProtocolConstant.CHECK_NAME_RES);
				}
			}else{
				MSGResult msgres = new MSGResult();
				msgres.setData("未知命令");
		    	msgres.setErr(true);
		    	String sendstr = mapper.writeValueAsString(msgres);
				sendMsg(ctx, sendstr, NetProtocolConstant.MSG_EORRE);
			}
		}
	}
	
	/**
	 * 发送数据
	 * @param channel 通道对象
	 * @param msgStr 要发送的数据字符串
	 * @param protocol 协议号
	 */
	public void sendMsg(Channel channel, String msgStr, int protocol){
		this.sendMsg(channel, msgStr.getBytes(CHARSET), protocol);
	}
	
	/**
	 * 发送数据
	 * @param channel 通道对象
	 * @param msgbytes 要发送的数据字节数组
	 * @param protocol 协议号
	 */
	public void sendMsg(Channel channel, byte[] msgbytes, int protocol){
        try {
        	if(channel != null && channel.isActive() && msgbytes != null && msgbytes.length > 0){
				ByteArrayOutputStream bos = new ByteArrayOutputStream(msgbytes.length + 8);
	
				bos.write(NetUtil.intToByteArray(msgbytes.length + 4));
				bos.write(NetUtil.intToByteArray(protocol));
//	        	bos.write(TypeChangeUtil.intToByteArray(0));//备用字段
				bos.write(msgbytes);
				
				ByteBuf resp = Unpooled.copiedBuffer(bos.toByteArray());
				ChannelFuture f = channel.writeAndFlush(resp);
				if(protocol == 99){
					f.addListener(ChannelFutureListener.CLOSE);
				}
        	}
		} catch (Exception e) {
			System.err.println("### 数据发送失败！！！");
			e.printStackTrace();
		}
	}
	
	/**
	 * 发送数据
	 * @param ctx 通道对象
	 * @param msgStr 要发送的数据字符串
	 * @param protocol 协议号
	 */
	public void sendMsg(ChannelHandlerContext ctx, String msgStr, int protocol){
        this.sendMsg(ctx, msgStr.getBytes(CHARSET), protocol);
	}
	
	/**
	 * 发送数据
	 * @param ctx 通道对象
	 * @param msgbytes 要发送的数据字节数组
	 * @param protocol 协议号
	 */
	public void sendMsg(ChannelHandlerContext ctx, byte[] msgbytes, int protocol){
        try {
        	if(ctx != null && ctx.channel() != null && ctx.channel().isActive() 
        			&& msgbytes != null && msgbytes.length > 0){
				ByteArrayOutputStream bos = new ByteArrayOutputStream(msgbytes.length + 8);
	
				bos.write(NetUtil.intToByteArray(msgbytes.length + 4));
				bos.write(NetUtil.intToByteArray(protocol));
//	        	bos.write(TypeChangeUtil.intToByteArray(0));//备用字段
				bos.write(msgbytes);
				
				ByteBuf resp = Unpooled.copiedBuffer(bos.toByteArray());
				ChannelFuture f = ctx.writeAndFlush(resp);
				if(protocol == 99){
					f.addListener(ChannelFutureListener.CLOSE);
				}
        	}
		} catch (Exception e) {
			System.err.println("### 数据发送失败！！！");
			e.printStackTrace();
		}
	}

	/**
	 * 在通道读取完成后会在这个方法里通知，对应可以做刷新操作 ctx.flush()
	 */
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
		//System.out.println("服务器readComplete 响应完成");
	}
	
	/**
	 * 捕获异常，当发生异常的时候，可以做一些相应的处理，比如打印日志、关闭链接
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
		System.err.println("服务器异常退出:\n" + cause.getMessage());
	}
	
}
